﻿using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using CashFlow.AzureRepository;
using CashFlow.Common;
using CashFlow.Entities;
using CashFlow.Repository;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.RelyingParty;

namespace CashFlow.Web.Controllers
{
	public class UserController : Controller
	{
		private OpenIdRelyingParty openIdProvider;
		private const string LoginViewName = "Login";
		private IAppIdentityRepository repository;
		private ITenantRepository tenantRepository;

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="T:UserController"/> class.
		/// </summary>
		public UserController()
		{
			openIdProvider = new OpenIdRelyingParty();
			repository = new AzureAppIdentityRepository();
			tenantRepository = new AzureTenantRepository();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="T:UserController"/> class.
		/// </summary>
		public UserController(
			IRelyingPartyApplicationStore store, 
			IAppIdentityRepository repository,
			ITenantRepository tenantRepository)
		{
			this.openIdProvider = new OpenIdRelyingParty(store);
			this.repository = repository;
			this.tenantRepository = tenantRepository;
		}

		/// <summary>
		/// Initializes data that might not be available when the constructor is called
		/// </summary>
		/// <param name="requestContext"></param>
		protected override void Initialize(RequestContext requestContext)
		{
			base.Initialize(requestContext);
		}
		#endregion

		public ActionResult Login()
		{
			ViewBag.ReturnUrl = Request.QueryString["ReturnUrl"];
			return View();

		}

		[ValidateInput(false)]
		public ActionResult Authenticate(string openid_identifier, string returnUrl)
		{
			//TODO: vvs p2 log exceptions
			ActionResult result = null;
			var response = openIdProvider.GetResponse();
			if (response == null)
			{
				result = HandleNullResponse(openid_identifier, returnUrl, result);
			}
			else
			{
				result = HandleResponse(response, returnUrl, result);
			}
			return result ?? new EmptyResult();
		}

		public ActionResult HandleNullResponse(string openid_identifier, string returnUrl, ActionResult result)
		{
			//BMK HandleNullResponse
			Identifier id;
			if (Identifier.TryParse(openid_identifier, out id))
			{
				try
				{
					var request = openIdProvider.CreateRequest(openid_identifier);
					request.AddExtension(new ClaimsRequest
					{
						FullName = DemandLevel.Require
					});
					result = request.RedirectingResponse.AsActionResult();
				}
				catch (ProtocolException ex)
				{
					ModelState.AddModelError("", "Error: Invalid identifier1");
					result = View(LoginViewName);
				}
			}
			else
			{
				ModelState.AddModelError("", "Invalid identifier");
				result = View(LoginViewName);
			}
			return result;
		}

		private ActionResult HandleResponse(IAuthenticationResponse response, string returnUrl, ActionResult result)
		{
			switch (response.Status)
			{
				case AuthenticationStatus.Authenticated:
					result = ProcessAuthenticateUser(response, returnUrl, result);
					break;
				case AuthenticationStatus.Canceled:
					ModelState.AddModelError("", "Canceled at provider");
					result = View("Login");
					break;
				case AuthenticationStatus.Failed:
					ModelState.AddModelError("", "response.Exception.Message");
					result = View("Login");
					break;
			}
			return result;
		}

		private ActionResult ProcessAuthenticateUser(IAuthenticationResponse response, string returnUrl, ActionResult result)
		{
			var alias = response.FriendlyIdentifierForDisplay;
			var sreg = response.GetExtension<ClaimsResponse>();

			var identity = new AppIdentity(response.ClaimedIdentifier)
			{
				FriendlyName = sreg.FullName
			};
			GetOrCreateUser(identity);
			
			FormsAuthenticationTicket authTicket = new
					FormsAuthenticationTicket(1,
					identity.TenantId,
					DateTime.Now,
					DateTime.Now.AddMinutes(30),
					false,
					identity.FriendlyName);

			var encTicket = FormsAuthentication.Encrypt(authTicket);

			Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

			if (!String.IsNullOrEmpty(returnUrl))
			{
				result = Redirect(returnUrl);
			}
			else
			{
				result = RedirectToAction("Index", "Home");
			}
			return result;
		}

		private void GetOrCreateUser(AppIdentity identity)
		{
			identity.TenantId = repository.GetTenantId(identity.Provider, identity.EncodedIdentifier);
			if (string.IsNullOrEmpty(identity.TenantId))
			{
				identity.TenantId = KeyGenerator.NewUserId();
				var tenant = new Tenant(identity.TenantId);
				repository.Create(identity);
				tenantRepository.Create(tenant);
			}
		}
	}
}